home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 June / Macworld (1999-06).dmg / Shareware World / Info / For Developers / MacZoop2.0.sea / MacZoop2.0 / Required Classes / ZoopUtilities.cpp < prev    next >
Text File  |  1999-02-23  |  18KB  |  654 lines

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            MacZoop 2.0 - "the framework for the rest of us"         
  5. *
  6. *
  7. *
  8. *            ZoopUtilities.cpp        -- general utilities
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1999, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21. #include    "MacZoop.h"
  22. #include    <FixMath.h>
  23. #include    <fp.h>
  24. #include    <palettes.h>
  25.  
  26.  
  27.  
  28. Rect                gZoomFXSourceRect = { 0, 0, 0, 0 };
  29.  
  30.  
  31. /*--------------------------------***  COPYPSTRING  ***---------------------------------*/
  32. /*    
  33. copy <srcString> to <destString>
  34. ----------------------------------------------------------------------------------------*/
  35.  
  36. void    CopyPString( ConstStr255Param srcString, Str255 destString )
  37. {
  38.     BlockMoveData( srcString, destString, MIN( srcString[0] + 1, 255 ));
  39. }
  40.  
  41.  
  42. /*------------------------------***  CONCATPSTRINGS  ***--------------------------------*/
  43. /*    
  44. concatenate <append> to <root>
  45. ----------------------------------------------------------------------------------------*/
  46.  
  47. void    ConcatPStrings( Str255 root, ConstStr255Param append )
  48. {
  49.     short charsToCopy;
  50.  
  51.     // Truncate if concatenated string would be longer than 255 chars.
  52.  
  53.     charsToCopy = MIN( append[0], 255 - root[0]);
  54.     BlockMoveData( append + 1, root + root[0] + 1, (long) charsToCopy);
  55.     root[0] += charsToCopy;
  56. }
  57.  
  58.  
  59. /*-----------------------------***  COPYPSTRINGTRUNC  ***-------------------------------*/
  60. /*    
  61. Copy <srcString> to <destString>, limiting maximum length of destString to <ccLim>
  62. ----------------------------------------------------------------------------------------*/
  63.  
  64. void    CopyPStringTrunc( ConstStr255Param srcString, Str255 destString, unsigned char ccLim )
  65. {
  66.     BlockMoveData( srcString, destString, MIN( srcString[0] + 1, ccLim + 1 ));
  67.     destString[0] = MIN( srcString[0], ccLim );
  68. }
  69.  
  70.  
  71. /*---------------------------***  CONCATPSTRINGSTRUNC  ***------------------------------*/
  72. /*    
  73. Concatenate <append> to <root>, but limiting total string length to a maximum of <ccLim>
  74. ----------------------------------------------------------------------------------------*/
  75.  
  76. void    ConcatPStringsTrunc( Str255 root, ConstStr255Param append, unsigned char ccLim )
  77. {
  78.     short charsToCopy;
  79.  
  80.     // Truncate if concatenated string would be longer than <ccLim> chars.
  81.  
  82.     charsToCopy = MIN( append[0], ccLim - root[0]);
  83.     BlockMoveData( append + 1, root + root[0] + 1, (long) charsToCopy);
  84.     root[0] += charsToCopy;
  85. }
  86.  
  87.  
  88. /*------------------------------***  COPYCTOPSTRING  ***--------------------------------*/
  89. /*    
  90. converts a 'C' null-terminated string up to 255 chars long to a Mac-style pascal string.
  91. Note that unlike c2pstr, etc, this does not modify the original string.
  92. ----------------------------------------------------------------------------------------*/
  93.  
  94. void    CopyCToPString( char* cStringIn, Str255 pStringOut )
  95. {
  96.     unsigned char u = 0;
  97.     
  98.     while( cStringIn[u++] ){};    // no error here, ignore warning.
  99.     
  100.     if ( u > 255 )
  101.         u = 255;
  102.         
  103.     BlockMoveData( cStringIn, &pStringOut[1], u );
  104.     
  105.     pStringOut[0] = u;
  106. }
  107.  
  108.  
  109. /*--------------------------------***  ISCOLOURPORT  ***--------------------------------*/
  110. /*    
  111. returns TRUE if the grafport passed is colour, otherwise FALSE
  112. ----------------------------------------------------------------------------------------*/
  113.  
  114. Boolean    IsColourPort( GrafPtr aPort )
  115. {
  116.     return (( aPort->portBits.rowBytes & 0x8000 ) != 0 );
  117. }
  118.  
  119.  
  120. /*------------------------------***  SETPORTBLACKWHITE  ***-----------------------------*/
  121. /*    
  122. sets the current port to black & white
  123. ----------------------------------------------------------------------------------------*/
  124.  
  125. void    SetPortBlackWhite()
  126. {
  127.     RGBForeColor( &gBlack );
  128.     RGBBackColor( &gWhite );
  129. }
  130.  
  131.  
  132. /*-------------------------------***  SETHILITEMODE  ***--------------------------------*/
  133. /*    
  134. set up hiliting mode for inversion operation
  135. ----------------------------------------------------------------------------------------*/
  136.  
  137. void    SetHiliteMode()
  138. {
  139.     LMSetHiliteMode( LMGetHiliteMode() & pHiliteBit );
  140. }
  141.  
  142.  
  143. /*----------------------------------***  MACHASDM  ***----------------------------------*/
  144. /*    
  145.  
  146. static function returns TRUE if Drag Manager is present. Checks link on PowerMacs.
  147. ----------------------------------------------------------------------------------------*/
  148.  
  149. Boolean        MacHasDM()
  150. {
  151.     // returns TRUE if drag manager is installed on this mac and is loaded by the CFM.
  152.     
  153.     Boolean hasDM = gMacInfo.hasDragManager;
  154.     
  155.     #if GENERATINGCFM 
  156.         // check that the dragLib is actually loaded and linked
  157.         
  158.         hasDM = hasDM && ( NewDrag != (void*) kUnresolvedCFragSymbolAddress );
  159.  
  160.     #endif
  161.  
  162.     return hasDM;
  163. }
  164.  
  165.  
  166.  
  167. /*--------------------------------***  NOTIFYALERT  ***---------------------------------*/
  168. /*    
  169. Works just like Alert(), except that if the app is in the background, the notification
  170. manager is used to inform the user that the app requires attention.
  171. ----------------------------------------------------------------------------------------*/
  172.  
  173. static NMRec    gNotification;
  174. static Str255    gNotifyMessage;
  175. static Boolean    gNotificationPosted = FALSE;
  176.  
  177. short        NotifyAlert( const short alertID, NTAlertFlags ntFlags )
  178. {
  179.     // basically: if ( background ):
  180.     //                install notification
  181.     //                wait for app foreground
  182.     //                delete notification
  183.     
  184.     if ( ! gNotificationPosted )
  185.     {
  186.         if ( gApplication->InBackground())
  187.         {
  188.             // if an alert is to be displayed, set it up
  189.             
  190.             if ( ntFlags & ntAlertDisplayMessage )
  191.             {
  192.                 // build a string that says "The application “<name>” requires your attention.
  193.                 // Please bring it to the front.
  194.                 
  195.                 Str255    appName;
  196.                 
  197.                 GetIndString( gNotifyMessage, kMiscStrListID, 17 );
  198.                 gApplication->GetName( appName );
  199.                 ConcatPStrings( gNotifyMessage, appName );
  200.                 GetIndString( appName, kMiscStrListID, 18 );
  201.                 ConcatPStrings( gNotifyMessage, appName );
  202.                 
  203.                 gNotification.nmStr = gNotifyMessage;
  204.             }
  205.             else
  206.                 gNotification.nmStr = NULL;
  207.             
  208.             // set up any sound:
  209.             
  210.             if ( ntFlags & ntAlertPlaySound )
  211.                 gNotification.nmSound = ( Handle ) -1L;
  212.             else
  213.                 gNotification.nmSound = NULL;
  214.             
  215.             Handle    appIconSuiteH;
  216.             OSErr    theErr;
  217.                 
  218.             // set up icon and menu mark
  219.             
  220.             gNotification.qType = 8;
  221.             gNotification.nmMark = TRUE;
  222.             gNotification.nmResp = NULL;
  223.             gNotification.nmRefCon = NULL;
  224.             
  225.             theErr = GetIconSuite( &appIconSuiteH, kApplicationIconSuiteID, svAllAvailableData );
  226.             
  227.             if ( theErr == noErr )
  228.                 gNotification.nmIcon = appIconSuiteH;
  229.             else
  230.                 gNotification.nmIcon = NULL;
  231.                 
  232.             // install notification:
  233.  
  234.             NMInstall( &gNotification );
  235.                 
  236.             gNotificationPosted = TRUE;
  237.             
  238.             // handle events until we come back to the front:
  239.         
  240.             gApplication->WaitApplicationForeground();
  241.             
  242.             // we're back, so delete the notification
  243.             
  244.             NMRemove( &gNotification );
  245.             gNotificationPosted = FALSE;
  246.             
  247.             if ( appIconSuiteH )
  248.                 DisposeIconSuite( appIconSuiteH, FALSE );
  249.         }
  250.         
  251.         if ( alertID > 0 )
  252.         {
  253.             StopCursorAnimation();
  254.             SetCursorShape( ARROW_CURSOR );
  255.             
  256.             return Alert( alertID, NULL );
  257.         }
  258.         else
  259.             return ok;
  260.     }
  261.     else
  262.     {
  263.         // what can we do? This shouldn't arise in normal use, but just in case, we act as
  264.         // though the user saw an alert and clicked OK. Not ideal, but since a notification
  265.         // can't interrupt another, there's little choice.
  266.         
  267.         SysBeep( 1 );
  268.         return ok;
  269.     }
  270. }
  271.  
  272.  
  273. /*---------------------------------***  MZDELAY  ***------------------------------------*/
  274. /*    
  275. delay for <ticks>. Header invariant version of toolbox Delay()
  276. ----------------------------------------------------------------------------------------*/
  277.  
  278. void    MZDelay( short ticks )
  279. {
  280.     #ifdef __COMPATIBILITY__
  281.         long ignored;
  282.     #else
  283.         unsigned long ignored;
  284.     #endif
  285.     
  286.     Delay( ticks, &ignored );
  287. }
  288.  
  289.  
  290. /*----------------------------------***  MZWAIT  ***------------------------------------*/
  291. /*    
  292. Wait for <ticks>. This continues to process events duringthe wait period
  293. ----------------------------------------------------------------------------------------*/
  294.  
  295. void    MZWait( unsigned short ticks )
  296. {
  297.     long    tc = TickCount() + ticks;
  298.     
  299.     while( TickCount() < tc )
  300.         gApplication->Process1Event();
  301. }
  302.  
  303.  
  304. /*---------------------------------***  ASSERTERR  ***----------------------------------*/
  305. /*    
  306. report an assertion error to the user via an alert
  307. ----------------------------------------------------------------------------------------*/
  308.  
  309. void    AssertErr( long lineNo, char* srcFile, char* reason, long val )
  310. {
  311. #if _DEBUG_
  312.     
  313.     Str15            lineStr, valStr;
  314.     Str255             fileStr, reasonStr;
  315.     
  316.     NumToString( lineNo, lineStr );
  317.     NumToString( val, valStr );
  318.     
  319.     CopyCToPString( srcFile, fileStr );
  320.     CopyCToPString( reason, reasonStr );
  321.         
  322.     ParamText( lineStr, fileStr, reasonStr, valStr );
  323.     StopCursorAnimation();
  324.     
  325.     (void) Alert( kAssertionAlertID, NULL );
  326.     
  327.     FailOSErr( kSilentErr );
  328.     
  329. #endif
  330. }
  331.  
  332. /*----------------------------***  SETGLOBALZOOMSOURCE  ***-----------------------------*/
  333. /*    
  334. set the zoom source to the passed global rect
  335. ----------------------------------------------------------------------------------------*/
  336.  
  337. void    SetGlobalZoomSource( Rect* aGlobalRect )
  338. {
  339.     gZoomFXSourceRect = *aGlobalRect;
  340. }
  341.  
  342.  
  343. /*-----------------------------***  SETLOCALZOOMSOURCE  ***-----------------------------*/
  344. /*    
  345. set the zoom source to the passed local rectangle
  346. ----------------------------------------------------------------------------------------*/
  347.  
  348. void    SetLocalZoomSource( Rect* aLocalRect )
  349. {
  350.     Rect    r = *aLocalRect;
  351.     
  352.     LocalToGlobal( &topLeft( r ));
  353.     LocalToGlobal( &botRight( r ));
  354.     
  355.     SetGlobalZoomSource( &r );
  356. }
  357.  
  358.  
  359. /*----------------------------------***  EQUALMEM  ***----------------------------------*/
  360. /*    
  361. compare two blocks of memory and return TRUE if they are equal. Zero-length data is not
  362. considered equal.
  363. ----------------------------------------------------------------------------------------*/
  364.  
  365. Boolean        EqualMem( void* a, void* b, const unsigned long length )
  366. {
  367.     Boolean        result = (length > 0);
  368.     Ptr            aa, bb;
  369.     
  370.     register unsigned long    len = length;
  371.     
  372.     aa = (Ptr) a;
  373.     bb = (Ptr) b;
  374.     
  375.     while( len-- )
  376.     {
  377.         if ( *aa++ != *bb++ )
  378.         {
  379.             result = FALSE;
  380.             break;
  381.         }
  382.     }
  383.  
  384.     return result;
  385. }
  386.  
  387.  
  388. /*--------------------------------***  EQUALHANDLE  ***---------------------------------*/
  389. /*    
  390. compare the contents of two handles and return TRUE if they're equal. If one or both
  391. handles are NULL, this returns FALSE. Handles with differing sizes are not considered
  392. equal even if the smaller handle's data matches the other handle's data exactly for its
  393. length. Internally this calls EqualMem above. If both handles are empty or zero sized,
  394. this will return FALSE, even though they are "equal" in one sense.
  395. ----------------------------------------------------------------------------------------*/
  396.  
  397. Boolean        EqualHandle( Handle a, Handle b )
  398. {
  399.     long    siza, sizb;
  400.     
  401.     if ( a == NULL || b == NULL )
  402.         return FALSE;
  403.         
  404.     siza = GetHandleSize( a );
  405.     sizb = GetHandleSize( b    );
  406.     
  407.     if ( siza != sizb )
  408.         return FALSE;
  409.     
  410.     // n.b-- no need to lock handles- EqualMem cannot move memory.
  411.         
  412.     return    EqualMem( *a, *b, siza );
  413. }
  414.  
  415.  
  416. /*--------------------------------***  SCALE2RECTS  ***---------------------------------*/
  417. /*    
  418. this function resizes <theRect> so that it will fit into <refRect>, but preserving its
  419. original aspect ratio. This is useful for preventing unwanted stretching of pictures
  420. both for creation and display of preview images. Note this also centres the rect within
  421. the refRect for the shorter dimension. This is the more intuitively correct behaviour
  422. ----------------------------------------------------------------------------------------*/
  423.  
  424. void    Scale2Rects( Rect *theRect, Rect *refRect )
  425. {
  426.     Fixed     aspectRatio;
  427.     Rect    destFrame;
  428.     short    refWidth,refHeight;
  429.     
  430.     destFrame.top = destFrame.left = 0;
  431.     OffsetRect(theRect,-theRect->left,-theRect->top);
  432.     
  433.     refWidth = refRect->right - refRect->left;
  434.     refHeight = refRect->bottom - refRect->top;
  435.     
  436.     if (theRect->right < theRect->bottom)
  437.     {
  438.         // the rect is taller than it is wide, so we centre horizontally
  439.         
  440.         aspectRatio = FixRatio(theRect->right,theRect->bottom);
  441.         destFrame.right = FixRound(FixMul(aspectRatio,FixRatio(refHeight,1)));
  442.         destFrame.bottom = refHeight;
  443.         
  444.         OffsetRect(&destFrame,refRect->left + ((refWidth - destFrame.right) / 2),refRect->top);
  445.     }
  446.     else
  447.     {
  448.         // the rect is wider than it is tall, so we centre vertically 
  449.         
  450.         aspectRatio = FixRatio(theRect->bottom,theRect->right);
  451.         destFrame.bottom = FixRound(FixMul(aspectRatio,FixRatio(refWidth,1)));
  452.         destFrame.right = refWidth;
  453.         
  454.         OffsetRect(&destFrame,refRect->left,refRect->top + ((refHeight - destFrame.bottom) / 2));
  455.     }
  456.     *theRect = destFrame;
  457. }
  458.  
  459.  
  460. /*-------------------------------***  REALTOSTRING  ***---------------------------------*/
  461. /*    
  462. converta real value <num>, into a pascal string, to <decPlaces> accuracy
  463. ----------------------------------------------------------------------------------------*/
  464.  
  465.  
  466. void RealToString( const double num, Str255& str, short decPlaces )
  467. {
  468.     decimal     d;
  469.     decform        df;
  470.     
  471.     df.style = FIXEDDECIMAL;
  472.     df.digits = decPlaces;
  473.     
  474.     num2dec( &df, num, &d );
  475.     dec2str( &df, &d, (char*) &str[1] );
  476.     
  477.     // set length of pascal string by scanning until we reach null terminator
  478.     
  479.     str[0] = 0;
  480.     while( str[++str[0]] ){};        // no error here, ignore warning.
  481. }
  482.  
  483.  
  484. /*-------------------------------***  FRAMEGRAYRECT  ***---------------------------------*/
  485. /*    
  486. draw a 3D effect frame around <aRect>
  487. ----------------------------------------------------------------------------------------*/
  488.  
  489.  
  490.  
  491. void    FrameGrayRect( Rect* aRect )
  492. {
  493.     Rect        globRect;
  494.     GDHandle    aDev;
  495.     RGBColor    aColour;
  496.     RGBColor    bColour;
  497.     
  498.     // frames a rectangle using two shades of gray so that the rectangle appears to
  499.     // be recessed into a gray surface. This actually draws 1 pixel outside the
  500.     // passed rectangle, so that normal FrameRect calls work as expected in addition.
  501.     
  502.     PenNormal();
  503.     
  504.     globRect = *aRect;
  505.     LocalToGlobal( &topLeft( globRect ));
  506.     LocalToGlobal( &botRight( globRect ));
  507.     aDev = GetMaxDevice( &globRect );
  508.     
  509.     GetBackColor( &aColour );
  510.     bColour.red = bColour.green = bColour.blue = 0xFFFF;
  511.     //GetGray( aDev, &aColour, &bColour );
  512.     
  513.     RGBForeColor( &bColour );
  514.     MoveTo( aRect->left, aRect->bottom );
  515.     LineTo( aRect->right, aRect->bottom );
  516.     LineTo( aRect->right, aRect->top );
  517.     
  518.     bColour.red = bColour.green = bColour.blue = 0;
  519.     GetGray( aDev, &aColour, &bColour );
  520.     RGBForeColor( &bColour );
  521.     
  522.     Move( 0, -1 );
  523.     LineTo( aRect->left - 1, aRect->top - 1 );
  524.     LineTo( aRect->left - 1, aRect->bottom );
  525.     
  526.     ForeColor( blackColor );
  527. }
  528.  
  529.  
  530.  
  531. /*-------------------------------***  ETCHGRAYRECT  ***---------------------------------*/
  532. /*    
  533. draw a 3D "etched" frame around <aRect>
  534. ----------------------------------------------------------------------------------------*/
  535.  
  536. void    EtchGrayRect( Rect* aRect )
  537. {
  538.     Rect        r, globRect;
  539.     GDHandle    aDev;
  540.     RGBColor    aColour;
  541.     RGBColor    bColour;
  542.     
  543.     // frames a rectangle using two shades of gray so that the rectangle appears to
  544.     // be recessed into a gray surface. This differs from FrameGrayRect in that it draws
  545.     //a 2-pixel rect that appears to recess into the surface, for decorative purposes.
  546.     
  547.     PenNormal();
  548.     
  549.     globRect = r = *aRect;
  550.     LocalToGlobal( &topLeft( globRect ));
  551.     LocalToGlobal( &botRight( globRect ));
  552.     aDev = GetMaxDevice( &globRect );
  553.     
  554.     GetBackColor( &aColour );
  555.     bColour.red = bColour.green = bColour.blue = 0xFFFF;
  556.     //GetGray( aDev, &aColour, &bColour );
  557.     
  558.     RGBForeColor( &bColour );
  559.     FrameRect( &r );
  560.     
  561.     bColour.red = bColour.green = bColour.blue = 0;
  562.     GetGray( aDev, &aColour, &bColour );
  563.     RGBForeColor( &bColour );
  564.     
  565.     OffsetRect( &r, -1, -1 );
  566.     FrameRect( &r );
  567.     
  568.     ForeColor( blackColor );
  569. }
  570.  
  571.  
  572. /*----------------------------***  GETMAINSCREENDEPTH  ***------------------------------*/
  573. /*    
  574. return pixel depth of main (startup) screen
  575. ----------------------------------------------------------------------------------------*/
  576.  
  577. short    GetMainScreenDepth()
  578. {
  579.     GDHandle    ms;
  580.     
  581.     ms = GetMainDevice();
  582.     
  583.     return (*(*ms)->gdPMap)->pixelSize;
  584. }
  585.  
  586.  
  587. /*-------------------------------***  SHIFTPATTERN  ***---------------------------------*/
  588. /*    
  589. cycle a black and white pattern
  590. ----------------------------------------------------------------------------------------*/
  591.  
  592. void    ShiftPattern( Pattern* aPat )
  593. {
  594.     unsigned char topRow, i;
  595.     
  596.     topRow = aPat->pat[0];
  597.     
  598.     for( i = 0; i < 7; i++ )
  599.         aPat->pat[i] = aPat->pat[i + 1];
  600.         
  601.     aPat->pat[7] = topRow;
  602. }
  603.  
  604.  
  605. /*--------------------------------***  ANTSREGION  ***----------------------------------*/
  606. /*    
  607. implement marching ants by framing the region in a fixed striped pattern. The pattern is
  608. cycled once each time this is called, and a timer is used to keep the rate constant- you
  609. can thus call this repeatedly to animate marching ants around a region. Be careful not
  610. to make the region too complex.
  611. ----------------------------------------------------------------------------------------*/
  612.  
  613. #define        kAntsRate    2
  614.  
  615. void    AntsRegion( RgnHandle aRgn )
  616. {
  617.     static Pattern            antsPat = { 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0xE1, 0xC3, 0x87 };
  618.     static unsigned long     antsTime = TickCount();
  619.     unsigned long            t;
  620.     PenState                ps;
  621.     
  622.     t = TickCount();
  623.     
  624.     if ( t >= ( antsTime + kAntsRate ))
  625.     {
  626.         antsTime = t;
  627.         
  628.         GetPenState( &ps );
  629.         
  630.         ShiftPattern( &antsPat );
  631.         PenPat( &antsPat );    
  632.         PenSize( 1, 1 );
  633.         PenMode( patCopy );
  634.     
  635.         FrameRgn( aRgn );
  636.         
  637.         SetPenState( &ps );
  638.     }
  639. }
  640.  
  641.  
  642. /*----------------------------***  BALLOONHELPREFRESH  ***------------------------------*/
  643. /*    
  644. force balloon help to update by hiding any visible balloon. If the balloon is still needed,
  645. it will be rebuilt and redisplayed, so you can call this if the item the balloon is
  646. pointing to changes state.
  647. ----------------------------------------------------------------------------------------*/
  648.  
  649. void        BalloonHelpRefresh()
  650. {
  651.     if ( HMIsBalloon() && HMGetBalloons())
  652.         HMRemoveBalloon();
  653. }
  654.